// 
//  Copyright 2011  Joel Johnson
// 
//    Licensed under the Apache License, Version 2.0 (the "License");
//    you may not use this file except in compliance with the License.
//    You may obtain a copy of the License at
// 
//        http://www.apache.org/licenses/LICENSE-2.0
// 
//    Unless required by applicable law or agreed to in writing, software
//    distributed under the License is distributed on an "AS IS" BASIS,
//    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
//    See the License for the specific language governing permissions and
//    limitations under the License.
using System;
using System.Linq;
using System.Text;
using System.Collections.Generic;

namespace TemplateCompiler {
	public class CSharpCompiler {
		private String filename;
		private List<Line> usings = new List<Line>();
		private List<Line> servletCode = new List<Line>();
		private List<Line> tags = new List<Line>();		
		
		public CSharpCompiler(String filename) {
			this.filename = filename;
			usings.Add(new UsingLine("using namespace='System'"));
			usings.Add(new UsingLine("using namespace='System.Linq'"));
			usings.Add(new UsingLine("using namespace='System.Collections.Generic'"));
			usings.Add(new UsingLine("using namespace='System.Text'"));
			usings.Add(new UsingLine("using namespace='Servlets.Api'"));
			usings.Add(new UsingLine("using namespace='Servlets.Attributes'"));
		}
		
		public void Parse(XmlDocument document) {
			List<Line> exploded = new List<Line>();
			ProcessDocument(exploded, document.Elements);
			servletCode.AddRange(exploded);
			
			Console.WriteLine (GetCSharpCode());
		}
		
		private String GetCSharpCode() {
			String ns = "GeneratedServlets";
			String className = "GeneratedServlet";
			String methodName = "GeneratedServletMethod";
			
			StringBuilder sb = new StringBuilder();
			foreach(Line line in usings) {
				sb.AppendLine(line.ToString());
			}
			sb.AppendLine("namespace "+ns+" {");
			sb.AppendLine("\t[Servlet]");
			sb.AppendLine("\tpublic class "+className+" {");
			sb.AppendLine("\t\t[GetMethod(\"/"+filename+"\")]");
			sb.AppendLine("\t\tpublic void "+methodName+"(IRequest request, IResponse response) {");
			sb.AppendLine("\t\t\tISession session = request.Session;");
			sb.AppendLine("\t\t\tResponseStream outstream = response.OutputStream;");
			
			if(tags.Count > 0) {
				sb.AppendLine("\t\t\tDictionary<String, String> tags = new Dictionary<String, String>();");
				foreach(Line line in tags) {
					sb.AppendLine("\t\t\t"+line.ToString());
				}
			}
			foreach(Line line in servletCode) {
				sb.AppendLine("\t\t\t"+line.ToString());
			}
			
			sb.AppendLine("\t\t}");
			sb.AppendLine("\t}");
			sb.AppendLine("}");
			
			return sb.ToString();
		}

		private void ProcessDocument(List<Line> result, IEnumerable<XmlObject> obj) {
			foreach(var o in obj) {
				if(o is XmlSpecial) {
					XmlSpecial s = o as XmlSpecial;
					AddSpecialLines(result, s);
				} else if(o is XmlContent) {
					XmlContent c = o as XmlContent;
					result.Add(new HtmlLine(c.Content));
				}
				else {
					if(o.Namespace == null) {
						result.Add(new HtmlLine(o.OpenTag()));
						ProcessDocument(result, o.Children);
						result.Add(new HtmlLine(o.CloseTag()));
					} else {
						AddCustomTagLines(result, o);
					}
				}
			}
		}
		
		private void  AddCustomTagLines(List<Line> result, XmlObject o) {
			String classname = o.Namespace;
			String methodname = o.Name;
			String context = "new TagContext(request, response, delegate(){";
			
			StringBuilder sb = new StringBuilder();
			o.AttributeNames.ToList().ForEach(a => 
					sb.Append("{\"").Append(a).Append("\",").Append(o.GetAttribute(a)).Append("},"));
			sb.Remove(sb.Length-1, 1);
			String parameterNames = sb.ToString();
			
			result.Add(new CodeLine(
				String.Format("TagManager.DoTag(tags[\"{0}\"], \"{1}\", {2}", classname, methodname, context)));
			ProcessDocument(result, o.Children);
			result.Add(new CodeLine("}, new Dictionary<string, object> {"+parameterNames+"}});"));
			//new Dictionary<object, object> {{"blah", "blah"}}
		}

		private void AddSpecialLines(List<Line> result, XmlSpecial s) {
			if(s.SpecialCharacter == "@") {
				if(UsingLine.IsUsingLine(s.Content)) {
					usings.Add(new UsingLine(s.Content));
				} else if(TagLine.IsTagLine(s.Content)) {
					tags.Add(new TagLine(s.Content));
				}else if(s.Content.Contains("MILK")) {
					
				}
				else {
					throw new NotSupportedException("@ line not supported");
				}
			} else if(s.SpecialCharacter == "=") {
				result.Add(new SugarCodeLine(s.Content));
			} else if(String.IsNullOrEmpty(s.SpecialCharacter)) {
				result.Add(new CodeLine(s.Content));
			} else {
				throw new NotSupportedException(s.SpecialCharacter + " line not supported");
			}
		}
	}
}

